/**
 * 
 */
package com.thor.palmmall.impl.pay;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.text.SimpleDateFormat;
import java.util.TreeMap;

import org.apache.commons.codec.binary.Hex;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.InputStreamEntity;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import com.alibaba.fastjson.JSON;
import com.google.gson.Gson;
import com.thor.palmmall.api.elecharges.EleChargesBill;
import com.thor.palmmall.api.elecharges.EleChargesBillState;
import com.thor.palmmall.api.pay.wechat.WeChatService;
import com.thor.palmmall.api.pay.wechat.WechatConfig;
import com.thor.palmmall.api.pay.wechat.entity.RsPaySignature;
import com.thor.palmmall.api.pay.wechat.entity.WeChatUnifiedOrderResponse;
import com.thor.palmmall.api.pay.wechat.entity.WechatPayNotify;
import com.thor.palmmall.api.pay.wechat.entity.WechatUnifiedOrder;
import com.thor.palmmall.dao.elecharges.EleChargesBillDao;
import com.thor.palmmall.dao.elecharges.PEleChargesBill;
import com.thor.palmmall.dao.elecharges.converter.EleChargesBillConverter;
import com.thor.palmmall.util.pay.SignUtil;
import com.thor.palmmall.util.pay.WebUtils;
import com.thor.palmmall.util.pay.XmlUtil;
import com.thor.palmmall.util.pay.tool;

/**
 * @author NuoSa
 *
 * @since 1.0
 */
public class WeChatServiceImpl implements WeChatService {
  private static Logger logger = LoggerFactory.getLogger(WeChatServiceImpl.class);

  private final String NOTIFY_FLAG_SUCCESS = "success";
  private final String NOTIFY_FLAG_FAILED = "failed";
  @Autowired
  private WechatConfig weChatConfig;

  @Override
  public RsPaySignature unifiedOrder(WechatUnifiedOrder unifiedOrder) {
    unifiedOrder.setAppid(weChatConfig.getAppId());
    unifiedOrder.setMch_id(weChatConfig.getMchId());
    unifiedOrder.setTrade_type(weChatConfig.getTradeType());
    unifiedOrder.setNotify_url(weChatConfig.getNotifyPay());
    unifiedOrder.setNonce_str(nonce_str(16));

    try {
      unifiedOrder.setSign(sign(SignUtil.bean2TreeMap(unifiedOrder)).toUpperCase());
      logger.info(new Gson().toJson(unifiedOrder));
      String tempXmlStr = XmlUtil.beanToXml(new ByteArrayOutputStream(), unifiedOrder);
      String requestXml = tempXmlStr != null ? tempXmlStr.substring(55) : "";
      logger.info("xml转义后内容:" + requestXml);
      InputStreamEntity inputStreamEntity = new InputStreamEntity(
          new ByteArrayInputStream(requestXml.getBytes("UTF-8")), ContentType.APPLICATION_XML);
      String resultXml = WebUtils.post(weChatConfig.getUnifiedorderUrl(), inputStreamEntity);
      logger.info("微信返回内容:" + resultXml);
      WeChatUnifiedOrderResponse resp = XmlUtil.xmlToBean(resultXml,
          WeChatUnifiedOrderResponse.class);
      if (resp != null) {
        resp.setTimeStamp(unifiedOrder.getTimeStamp());
      }

      RsPaySignature sign = new RsPaySignature();
      sign.setAppId(weChatConfig.getAppId());
      sign.setPackageStr("Sign=WXPay");
      sign.setPartnerId(weChatConfig.getMchId());
      sign.setPrepayId(resp.getPrepayId());
      sign.setSign(SignUtil.createSign(sign, weChatConfig.getMchKey()));
      return sign;
    } catch (UnsupportedEncodingException e) {
      e.printStackTrace();
    }
    return null;
  }

  @Override
  public boolean verifyNotify(String request) {
    try {
      WechatPayNotify notice = XmlUtil.xmlToBean(request, WechatPayNotify.class);
      if (notice == null) {
        return false;
      }
      logger.debug("微信回调参数:" + JSON.toJSONString(notice));
      String sign = SignUtil.createSign(notice, weChatConfig.getMchKey());
      boolean ischeck = sign.equals(notice.getSign());
      logger.debug("微信验签结果:" + ischeck);

      SimpleDateFormat fmt = new SimpleDateFormat("yyyyMMddHHmmss");
      EleChargesBill bill = eleChargesBillDao.getByBillNumber(notice.getOut_trade_no());// 获取后台通知的数据，其他字段也可用类似方式获取
      if (bill == null) {
        return false;
      }
      if (EleChargesBillState.FINISH.equals(bill.getState())) {
        return true;
      }

      PEleChargesBill billTOSave = EleChargesBillConverter.getInstance().convert(bill);
      if (ischeck) {
        bill.setActionTime(fmt.parse(notice.getTime_end()));
        bill.setNotifyFlag(NOTIFY_FLAG_SUCCESS); // 回调使用单独的标志字段
        bill.setState(EleChargesBillState.PAID);
        bill.setTransactioNo(notice.getTransaction_id());
        eleChargesBillDao.saveEleChargesBill(billTOSave, bill.getCreateInfo());
        return true;
      } else {
        bill.setNotifyFlag(NOTIFY_FLAG_FAILED);
        eleChargesBillDao.saveEleChargesBill(billTOSave, tool.getOperateInfo());
      }
      return false;
    } catch (Exception e) {
      e.printStackTrace();
    }
    return false;
  }

  /**
   * 微信nonce_str生成算法
   * 
   * @param bits
   *          生成位数,选择64bit
   * @return 生成后的nonce_str
   */
  public String nonce_str(int bits) {
    final byte[] bytes;
    try {
      SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
      if ((bits % 8) != 0) {
        throw new IllegalArgumentException("Size is not divisible by 8!");
      }
      bytes = new byte[bits / 8];
      secureRandom.nextBytes(bytes);
      return Hex.encodeHexString(bytes);
    } catch (NoSuchAlgorithmException e) {
      e.printStackTrace();
    }
    return System.currentTimeMillis() + "";
  }

  /**
   * 对请求进行签名
   * 
   * @param param
   *          要签名的参数
   * @return
   * @throws UnsupportedEncodingException
   */
  public String sign(TreeMap<String, ?> param) throws UnsupportedEncodingException {
    String paramUrl = SignUtil.joinKeyValue(new TreeMap<String, Object>(param), null,
        "&key=" + weChatConfig.getMchKey(), "&", true, "sign_type", "sign");
    logger.info("微信待签名串:" + paramUrl);
    MessageDigest digestUtils = DigestUtils.getMd5Digest();
    digestUtils.update(paramUrl.getBytes("UTF-8"));
    byte[] sign = digestUtils.digest();
    String result = Hex.encodeHexString(sign);
    logger.info("签名结果:" + result);
    return result;
  }

  @Autowired
  private EleChargesBillDao eleChargesBillDao;

}
